/*
###########################################################################
# File..................: WonAndDoneForAccountController
# Version...............: 1
# Created by............: Abhishek Tandon
# Created Date..........: 21-Nov-2011
# Last Modified by......: 
# Last Modified Date....: 
# Description...........: This Controller Control the Won and Done process of Creating
#                         Opportunity and Opportunty line items for an Account
# VF page...............: wonAndDoneForAccount.page               
# Change Log:               
#
# Copyright (c) 2000-2011. Astadia, Inc. All Rights Reserved.
#
# Created by the Astadia, Inc. Modification must retain the above copyright notice.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any commercial purpose, without fee, and without a written
# agreement from Astadia, Inc., is hereby forbidden. Any modification to source
# code, must include this paragraph and copyright.
#
# Permission is not granted to anyone to use this software for commercial uses.
#
# Contact address: 2839 Paces Ferry Road, Suite 350, Atlanta, GA 30339
# Company URL : http://www.astadia.com
###########################################################################
*/

public class WonAndDoneForAccountController {



  //To hold the account Details for which we are adding the Opps and product
    public Account account {set;get;}
    
    //To hold the Account id 
    private String accountId {set;get;}
    
    //Constant for accountid url parameter
    private String ACCOUNT_ID_PARAM = 'accId';
    
    //Opportunity which needs to be created
    public Opportunity opportunity {set;get;}
    
    // To hold the list of OLI's
    private List<OpportunityLineItem> opportunityLineItems =null;
     
    // list of all products 
    public List<Selectoption> products {get;set;} 
    
    // list of all Available contracts
    public List<Selectoption> contracts {get;set;} 
    
    //The count of number of product added
    public Integer numberOfProducts = -1;
    
    //Set of Product details
    public List<productDetails> productDetailsList {get;set;}
    
    //to hold the value of product to be removed
    public Integer selectedRowIndex {get;set;}
    
    // whether the number of product quantity exceeded
    public Boolean quantityExceeded {get;set;}
    
    //Maximum Quanity permitted per account via Won and Done in a Calendar Month
    private Integer MAX_QUANTITY_PER_ACCOUNT=10;
    
    // Price book name which is going to be used For adding Opportunity product
    private String PRICE_BOOK_NAME='Standard Price Book'; 
    
    //The sum of quanity of products for an account
    private Integer existingQuntityForAccount;
    /**
    * <P> Class constructor, it invokes and do following task. </P>
    * <OL>
    *    <li>Load the ccount object based on id given in the Querystring/URL</li>
    *    <li>populate the Opportunity Object with the account,stage and closed date</li>
    * </OL>
    */
    public WonandDoneForAccountController() {
   
            //fetching Account id from the url
            accountId = ApexPages.currentPage().getParameters().get(ACCOUNT_ID_PARAM);
            
            if(null!= accountId && ''!=accountId){
                
                //Fetch the Account object
                account= [select id,name from Account where id=:accountId ];
                
                //Fetching the existing quanity created on this month for the account
                AggregateResult aggResult= [Select sum(o.Quantity) total_quantity From OpportunityLineItem o WHERE o.CreatedDate = THIS_MONTH and o.Opportunity.AccountId=:accountId and o.Created_by_Won_and_Done_flow__c=true];
                existingQuntityForAccount=  (null==aggResult.get( 'total_quantity')?0:Integer.valueOf(aggResult.get( 'total_quantity')));
                System.debug('total_quantity:= ' +existingQuntityForAccount);
                
                loadConfigSetting();
                  
                //Populate the opportunity                
                if(null!=account && existingQuntityForAccount<MAX_QUANTITY_PER_ACCOUNT){
                        System.debug(';###################');
                    quantityExceeded = false;
                   opportunity = new Opportunity(AccountId=account.Id , stageName='Closed Won', Closedate=Datetime.now().date());
                  //populating product Drop down
                   populateProductPickList();
                   
                   //Populating contracts list
                   populateContracts();
                       
                   productDetailsList= new  List<productDetails>();     
                  //Adding one Product Line item by default
                  addProduct();
                }else{
                    // Since Quantity of Opportunity is more than 10 so adding error and redirecting the Account details page

                     quantityExceeded = true;
                     addErrorMessage('Maximum permitted quantity of products is ' +MAX_QUANTITY_PER_ACCOUNT+ ' in a calendar Month for an Account via Won and Done process and this account has already '+ existingQuntityForAccount +' products added for this month, So you have to use Normal Opportunity creation flow, click below link to go back to the Account.');
                     opportunity = new Opportunity(AccountId=account.Id);
                }
              
            
            }else{
                //When account id not fecthed from the URL parameter
                quantityExceeded = true;
                addErrorMessage('Opportunity Can not be created due to some Unkwnon reason, please go back to the Account and again start the Won and Done Process.');
            }                           
    }
    
    
    
        /**
        * </P> This method is used to remove the Selecetd rown item of product from the Selected product list, 
        *       when user click on the remove command link from the VF page.
        * </P>
        *  @return PageReference of same Page
        */
    public PageReference removeProductSelection() {
       System.debug('%%%%%%%%%%%%%%% selectedRowIndex '+ selectedRowIndex );
       try{
               if(null!=selectedRowIndex){
                        //numberOfProducts = numberOfProducts - 1;
                        List<ProductDetails> tempList= new  List<ProductDetails>(productDetailsList);
                        
                        for (Integer itr=0; itr<tempList.size(); itr++){
                                if(tempList.get(itr).index==selectedRowIndex){
                                        productDetailsList.remove(itr);
                                }
                        }
                     
               }else{
                
                 addErrorMessage('There is nothing to remove.');
               }
        
       }catch(Listexception lx){
         return null;
       }
        return null;
    }
    
    
    /**
        * </P> This method is used to redirect the page to the Parent Account page for which Won & Done process was initiated 
        *       when user click on the Cancel command button from the VF page.
        * </P>
        *  @return PageReference of Account page
        */
     public PageReference cancelWonAndDone() {
        
        if(null!= accountId && ''!=accountId){
                Pagereference pr= new Pagereference('/'+accountId);
                pr.setRedirect(true);
                
                
                return pr;
        }
        return Apexpages.currentPage();
    }

 
   /**
        * </P> This method is used to save the Opportunity and Opporunity Line items whne user click on the Save command Button
        *       from the VF page.
        * </P>
        *  @return PageReference of Opportunity created
        */
     public PageReference save() {
        
        
        if(null!= opportunity && null!= opportunity.AccountId){
            
            if(!validateProductQuantity()){
                
                 
                 return Apexpages.currentPage();
            }
              
           try{
                    // Create Opportunity
                    opportunity.description=opportunity.name;
                    Id opportunityId= saveAndReturnId (opportunity);
                    
                    // Create Opportunity Product and redirect to Opps page     
                   if(null!=opportunityId){
                     createOpportunityProduct(opportunityId,opportunity.name);
                     Pagereference pr= new Pagereference('/'+opportunityId);
                     pr.setRedirect(true);
                     return pr;
                   }
           }catch(System.DmlException ex){
                addErrorMessage(ex.getMessage());
           }
        }
        
         addErrorMessage('Opportunity Can not be created due to some Unkwnon reason, please go the account by clicking on Cancel button and again start the Won and Done Process.');
         return Apexpages.currentPage();
    }
   
  
    
    /**
    * <P> This method is used to populate the prodouct Pick list For SME .</P>
    */
    private void populateProductPickList(){
        products   = new List<Selectoption>();
        for (PricebookEntry pricebookEntry : [Select  p.ProductCode, p.Product2Id, p.Pricebook2Id, p.Name, p.Id From PricebookEntry p where p.Pricebook2.Name=:PRICE_BOOK_NAME]){
              products.add(new SelectOption(pricebookEntry.Id,pricebookEntry.Name));
        }    
        
    }
    
  /**
  * <P> This method is used to create a single Product Line item On VF page for Adding Opportunity product.</P>
  * @return PageReference of same Page
  */  
  public Pagereference addProduct(){
           
        numberOfProducts = numberOfProducts+1;
        System.debug('####### numberOfProducts:= '+ numberOfProducts);
        addOneMoreProduct();     
        return null; 
    }       
  
  /**
  * <P> This method is used to add a single ProductDetails item in the Product List.</P>
  */  
  private void addOneMoreProduct(){   
        
        ProductDetails productDetails = new ProductDetails(numberOfProducts);
                  
        //add the record to the inner class list
        productDetailsList.add(productDetails);    
        system.debug('lstInner---->'+productDetailsList);
  }
  
  
  /**
  * <P> This method is used to add Error message to the current Visualforce page, based on the Message text provided.</P>
  * 
  * @param: message as String that message to be added.
  */
    private void addErrorMessage(String message) {
       ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR,message));
       System.debug('Added Error Message := ' + Message );
                
   }
  
  /**
  * <P> This method is used to validate opportunity line items.</P> 
  * <OL> Following validations are applied
  *     <li> The total quanity of product can not exceed by 10</li>
  *     <li>Quantity can not be null or blank</li>
  * </OL>
  */
  private Boolean validateProductQuantity(){
    Boolean isVaild=true;
    Decimal totalQuantity=existingQuntityForAccount;
     System.debug('$$$$$$$$$$:= ' +existingQuntityForAccount);
    for(ProductDetails product : productDetailsList){
        System.debug('$$$$$$$$$$:= ' +product.oli.quantity);
        
        // quantity should not be blank or non zero and greater than zero
        if(null==product.oli.quantity || 0>=product.oli.quantity){
            product.oli.quantity.addError('Product quantity cannot be left blank or Less than 1.');
          //addErrorMessage('Product quantity can not be blank or 0.');
            isVaild = false;
            
        }
         System.debug('$$$$$$$$$$:= ' +product.oli.Unitprice);
        // Amount should not be blank and equal or greater than zero
        if(null==product.oli.Unitprice || 0>product.oli.Unitprice){
            addErrorMessage('Product amount cannot be left blank or have negative value.');
            isVaild = false;
            
        }
        /* 
        // contractTerm should not be blank or non zero
        if(null==product.contractTerm || 0==product.contractTerm){
            addErrorMessage('Contract term can not be blank or 0');
            isVaild = false;
            
        }
        */
        //Total quanity
        totalQuantity = totalQuantity + product.oli.quantity;
        if(totalQuantity>MAX_QUANTITY_PER_ACCOUNT){
            isVaild = false;
            String errorMessage='Maximum permitted quantity of products is ' +MAX_QUANTITY_PER_ACCOUNT+ ' in a calendar month for an Account via Won and Done process';
            if(0<existingQuntityForAccount){
            System.debug('With in if$$$$$$$$$$:= ' +existingQuntityForAccount);
        
            errorMessage= errorMessage +' and this account has already '+ existingQuntityForAccount +' product(s) added for this month';
            }
            errorMessage= errorMessage +', either reduce the quanity of products that you are adding or add it via normal opportunity creation flow.';
            addErrorMessage(errorMessage);
            
        }
        /*if(!isVaild){
            break;
        }*/
        
        
    }
    
    return isVaild;
  }
   /**
   * <P> This method is used to the save the SObject in salesforce and return the Id of that      
   *  object. </P>        
   *
   */
   private Id saveAndReturnId(Sobject sobj ){
        
         Database.Saveresult result= Database.insert(sobj);
         if(null != result && result.isSuccess()){
                
                return result.getId();
         }
        return null;
   }
   
   /**
   * <P> This method is used to the populate and Opportunity Line items for the Given Opportunity. </P>        
   * @param: opportnityId , Id of opportunity
   * @param: oppsName, name of Opportunity
   */
   private void createOpportunityProduct(Id opportnityId, String oppsName){
        opportunityLineItems = new List<OpportunityLineItem>();
        try{
        for(ProductDetails product : productDetailsList){
            
            product.oli.OpportunityId=opportnityId;
            product.oli.Description=oppsName;
            product.oli.ServiceDate= DateTime.now().date();
            product.oli.Term_months__c=Double.valueOf(product.contractTerm);
            //oli.OpportunityId=opportnityId;
            //oli.Quantity=product.quantity;
            //oli.UnitPrice=product.amount;
            //oli.PricebookEntryId=product.pricebookEntryId;
            //oli.Description=oppsName;
            //oli.ServiceDate= DateTime.now().date();
            
            opportunityLineItems.add(product.oli);
        }
        if(!opportunityLineItems.isEmpty()){
            
            insert opportunityLineItems;
        }
        }catch(Exception ex){
            
            addErrorMessage(ex.getMessage());
        }
   }
   
   /**
   * <P> This method is used to load the custom config settings from the Custom objects created with name 'Won_and_done_process_settings__c'
   *  it loads the following configurations for the Won and Done process. </P>
   *  <OL>
   *    <li> Load the price book name which to be used for Won & done Process</li>
   *    <li> Load the value of Maximum allowed quanity of products for an Account in a calendar month using Won & done Process</li>
   *  </OL>       
   */
   private void loadConfigSetting(){
        
                Won_and_done_process_settings__c priceBookSettings =Won_and_done_process_settings__c.getValues('Price_book_name');
                Won_and_done_process_settings__c productQuantitySetting =Won_and_done_process_settings__c.getValues('Maximum_product_quantity');
        System.debug(priceBookSettings);
        System.debug(productQuantitySetting);
        //Setting price book name if present in setting
        if(null != priceBookSettings ){
                PRICE_BOOK_NAME= (null!=priceBookSettings.get('Value__c')?String.valueOf(priceBookSettings.get('Value__c')):PRICE_BOOK_NAME);
        }
        
        if(null != productQuantitySetting ){
                MAX_QUANTITY_PER_ACCOUNT= (null!=productQuantitySetting.get('Value__c')?Integer.valueOf(productQuantitySetting.get('Value__c')):MAX_QUANTITY_PER_ACCOUNT);
        }
   }
   
   
  /**
  * <P> This Method is used to populate the list of Contract available.</P>
  *
  */ 
  
  private void populateContracts(){
        
        contracts= new List<Selectoption>();
        
        contracts.add(new SelectOption('1','1'));
         contracts.add(new SelectOption('3','3'));
         contracts.add(new SelectOption('12','12'));
         contracts.add(new SelectOption('18','18'));
         contracts.add(new SelectOption('24','24'));
         contracts.add(new SelectOption('36','36'));
         contracts.add(new SelectOption('60','60'));
         
        
  }
  /**
  * <P> Inner class to maintain the product information which is used to show and maintain the Opportunity product
  * Information from VF page to controller.
  * </P>
  */
    private class ProductDetails{
        
              //index of Object
              public Integer index {get;set;}
                        
              /*
              // id of selected product
              public String productId {get;set;}
                        
              // amount of the product selected
               public Double amount {get;set;}
                        
              // Quantity of the product selected
               public Integer quantity {get;set;}
               */       
              // Term of the product selected
               public String contractTerm {get;set;}
               
               public String pricebookEntryName {get;set;}  
              // id of selected PricebookEntryId
              //public String pricebookEntryId {get;set;}  
              
              //OLi object hold the values
              public OpportunityLineItem oli   {get;set;}       
              
              /*Inner Class Constructor*/
               public productDetails(Integer intCount){
                    this.index=intCount; 
                    //this.amount=0;
                    //this.quantity=0;
                    //this.contractTerm=0;
                    this.oli = new   OpportunityLineItem(Created_by_Won_and_Done_flow__c=true);
                                    
                    
                                           
                }/*End Inner class Constructor*/    
        }
}